package com.hmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.hmall.common.PageDto;
import com.hmall.common.client.ItemClient;
import com.hmall.item.pojo.Item;
import com.hmall.search.dto.SearchDto;
import com.hmall.search.pojo.ItemDoc;
import com.hmall.search.service.ISearchService;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHost;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchService implements ISearchService {

    private RestHighLevelClient restHighLevelClient =
            new RestHighLevelClient(RestClient.builder(HttpHost.create("http://127.0.0.1:9200")));
        @Resource
        private ItemClient itemClient;
    // 分页搜索
    @Override
    public PageDto<ItemDoc> search(SearchDto params) throws Exception {
        SearchRequest searchRequest=new SearchRequest("item");
        buildBasicQuery(params, searchRequest);
        //分页
        searchRequest.source().from((params.getPage()-1)*params.getSize())
                .size(params.getSize());
        SearchResponse searchResponse =restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //条数
        long total = searchResponse.getHits().getTotalHits().value;
        //获取文档数组
        SearchHit[] hits = searchResponse.getHits().getHits();
        List<ItemDoc> ItemDocs = Arrays.stream(hits).map(hit ->
                {
                    String sourceJson = hit.getSourceAsString();
                    //解析反序列化
                    ItemDoc itemDoc = JSON.parseObject(sourceJson, ItemDoc.class);
                    itemDoc.setPrice(itemDoc.getPrice()*100);
                    //高亮结果解析
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    HighlightField field = highlightFields.get("name");
                    if (field !=null)
                    {
                        itemDoc.setName(field.getFragments()[0].toString());
                    }
                    return itemDoc;
                }
        ).collect(Collectors.toList());
        return new PageDto<>(total,ItemDocs);
    }
    private void buildBasicQuery(SearchDto params, SearchRequest request) {
        //构建boolean查询条件
        SearchSourceBuilder searchSourceBuilder = request.source();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //boolQuery.must(QueryBuilders.matchAllQuery());
        //条件查询
        String key = params.getKey();
        if (StringUtils.isNotBlank(key))
        {
            boolQuery.must(QueryBuilders.matchQuery("all",key));
        }
        //品牌过滤
        String brand = params.getBrand();
        if(StringUtils.isNotBlank(brand))
        {
            boolQuery.filter(QueryBuilders.termQuery("brand", brand));
        }
        //价格过滤
        Long maxPrice = params.getMaxPrice();
        Long minPrice = params.getMinPrice();
        if (minPrice!=null && maxPrice!=null)
        {
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(maxPrice).gte(minPrice));
        }
        //分类过滤
        String category = params.getCategory();
        if (StringUtils.isNotBlank(category))
        {
            boolQuery.filter(QueryBuilders.termQuery("category", category));
        }
        //排序
        String sortBy = params.getSortBy();
        if ("sold".equals(sortBy))
        {
            searchSourceBuilder.sort(sortBy, SortOrder.DESC);
        }else if ("price".equals(sortBy))
        {
           searchSourceBuilder.sort(sortBy, SortOrder.ASC);
        }
        //高亮
            searchSourceBuilder.highlighter(new HighlightBuilder().field("name")
                    .preTags("<em style='color:blue'>")
                    .postTags("</em>")
                    .requireFieldMatch(false));
        //算分函数
        FunctionScoreQueryBuilder.FilterFunctionBuilder[] filterFunctionBuilders={
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                        QueryBuilders.termQuery("isAD",true),
                        ScoreFunctionBuilders.weightFactorFunction(10)
                )};
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(boolQuery,filterFunctionBuilders);
        searchSourceBuilder.query(functionScoreQuery);
    }
    //获取筛选条件
    @Override
    public Map<String, List<String>> getFilters(SearchDto params) throws Exception {
        SearchRequest searchRequest=new SearchRequest("item");
        buildBasicQuery(params, searchRequest);
        // 2.2.构造条件size
        searchRequest.source().size(0);
        // 2.3.聚合条件
        buildAggregation(searchRequest);
        // 3.发出请求
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 4.解析结果
        Aggregations aggregations = response.getAggregations();
        // 4.1.解析品牌,获取品牌分桶数据
        List<String> brandList = getAggByName(aggregations, "brand");
        // 4.2.解析分类，获取分类分桶数据
        List<String> categoryList = getAggByName(aggregations, "category");
        Map<String, List<String>> result = new HashMap<>(2);
        result.put("brand", brandList);
        result.put("category", categoryList);
        return result;
    }
    /**
     *
     * @param aggregations
     * @param name
     * @return
     */
    private List<String> getAggByName( Aggregations aggregations, String name) {
        Terms term = aggregations.get(name);
        List<? extends Terms.Bucket> buckets = term.getBuckets();
        List<String> list = buckets.stream().map(bucket ->
        {
            String keyAsString = bucket.getKeyAsString();
            return keyAsString;
        }).collect(Collectors.toList());
        return list;
    }

    /**
     * 聚合条件
     * @param searchRequest
     */
    private void buildAggregation(SearchRequest searchRequest) {
        searchRequest.source()
                .aggregation(AggregationBuilders.terms("brand").field("brand").size(35))
                .aggregation(AggregationBuilders.terms("category").field("category").size(35));
    }


    //获取关键词推荐
    @Override
    public List<String> getSuggestion(String key) throws Exception {
        //创建请求
        SearchRequest searchRequest=new SearchRequest("item");
        //设置搜索条件
        searchRequest.source().suggest(new SuggestBuilder()
                .addSuggestion("itemSuggestion", SuggestBuilders
                        .completionSuggestion("suggestion")
                        .size(10)
                        .skipDuplicates(true)
                        .prefix(key)

        ));
        //发送请求
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //解析响应
        Suggest suggest = searchResponse.getSuggest();
     CompletionSuggestion suggestion = suggest.getSuggestion("itemSuggestion");
        List<String> list = suggestion.getOptions().stream().map(option -> {
            String text = option.getText().toString();
            return  text;
        }).collect(Collectors.toList());
        return list;
    }

    /**
     * 上架
     * @param id
     */
    @SneakyThrows
    @Override
    public void saveItemById(Long id) {
        Item item = itemClient.findById(id);
        ItemDoc itemDoc=new ItemDoc(item);
        String jsonObject = JSON.toJSONString(itemDoc);
       IndexRequest indexRequest=new IndexRequest("item").id(id.toString());
        indexRequest.source(jsonObject, XContentType.JSON);
        restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
    }

    /**
     * 下架
     * @param id
     */
    @SneakyThrows
    @Override
    public void deleteItemById(Long id) {
        DeleteRequest deleteRequest=new DeleteRequest("item",id.toString());
        restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
    }
}
